/*
 * Copyright (c) 2014. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

package com.net.NettyEngine3.core;



import org.jboss.netty.bootstrap.Bootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.MemoryAwareThreadPoolExecutor;
import org.jboss.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA.
 * User: CHENLEI
 * Date: 12-10-30
 * Time: 上午11:03
 * To change this template use File | Settings | File Templates.
 * start gameSever Socket
 */
@Service
public final class NServerFrameNettyStart implements INettyServer {
    private static final Logger LOG = LoggerFactory.getLogger(NServerFrameNettyStart.class);
    public static boolean debug;
    private ServerBootstrap bootstrap=null;
    private final String[]optionList={"tcpNoDelay","keepAlive","readWriteFair","child.tcpNoDelay","child.reuseAddress","readWriteFair"};
    private ExecutionHandler executionHandler=null;

    public NServerFrameNettyStart(){

    }

    @Override
    public void startServer(){
        try{
            getServerBootstrap();
            configureServerBootStrap(this.bootstrap,optionList);
        }catch (Exception e){
            LOG.error("netty start failed ", e);
            this.releaseServerResources();
        }

    }

    /**
     * 初始化server配置
     *
     * @return boolean
     */
    @Override
    @PostConstruct
    public boolean IntiServer() {
//        NServerConfig mServerConfig=new NServerConfig();
//        return mServerConfig.IntiServerConfig();
        return false;
    }

    /**
     * If thread pools or TCP/IP parameters or the pipeline factory need to be
     * modified then it is this method that needs to be overriden.
     *
     * @param optionsList Used to set tcp ip options like noDelay etc.
     */
    @Override
    public void configureServerBootStrap(ServerBootstrap bootstrap,String[] optionsList) {
        // For clients who do not use spring.
        if(null == bootstrap){
            getServerBootstrap();
        }
        if (bootstrap != null){
            bootstrap.setPipelineFactory(getPipelineFactory());
            if (null != optionsList && optionsList.length > 0){
                for (String option : optionsList){
                    bootstrap.setOption(option, true);
                }
            }
            bootstrap.setOption("receiveBufferSize", 0x100000 * 0x40);
            bootstrap.setOption("child.receiveBufferSize", 0x100000 * 0x40);
            bootstrap.setOption("tcpNoDelay", 0);//Runtime ClassCastException
            bootstrap.bind(new InetSocketAddress(9999));
        }
    }

    /**
     * createServerBootstrap will create a pipeline factory and save it as a
     * class variable. This method can then be used to retrieve that value.
     *
     * @return Returns the channel pipeline factory that is associated with this
     *         netty server.
     *
     *   ExecutionHandler将自己管理的一个线程池中拿出一个线程来处理排在它后面的业务逻辑handler。
     *   而worker线程在经过ExecutionHandler后就结束了，
     *   它会被ChannelFactory的worker线程池所回收。
     *   业务数据递交给此线程池处理；
     *   考虑到发送的数据包缓存队列
     */
    @Override
    public ChannelPipelineFactory getPipelineFactory() {
        //针对待处理的业务数据buf将在objectSession中被缓存，故  maxChannelMemorySize ，maxTotalMemorySize
        //设置为disabled ，the value of code 0;
        // Deadlock can happen when MemoryAwareThreadPoolExecutor with limit is used，
        // fixed by netty 3.6.6.final
        executionHandler = new ExecutionHandler(
                new MemoryAwareThreadPoolExecutor(Runtime.getRuntime().availableProcessors()+1,
                        0, 0,0, TimeUnit.SECONDS,new PriorityThreadFactory("logic_handle",Thread.NORM_PRIORITY+1)));
        ChannelHandler idleStateHandler=new IdleStateHandler(NServerFrameUtil.timer,10,0,0,TimeUnit.SECONDS);
        return new NServerFrameChannelPipelineFactory(idleStateHandler,executionHandler);
    }


    /**
     * main_reactor and  sub_reactor
     * @return
     */
    @Override
    public Bootstrap getServerBootstrap() {
        ChannelFactory factory=new NioServerSocketChannelFactory(Executors.newFixedThreadPool(0x1,new PriorityThreadFactory("@+main_reactor线程池+@",Thread.NORM_PRIORITY)),
                Executors.newCachedThreadPool(new PriorityThreadFactory("@+sub_reactor线程池+@",Thread.NORM_PRIORITY)));
        bootstrap = new ServerBootstrap(factory);
        return bootstrap;
    }

    /**
     * 释放资源
     */
    @Override
    public final void releaseServerResources(){
        this.executionHandler.releaseExternalResources();
        bootstrap.releaseExternalResources();
        NServerFrameUtil.timer.stop();
        NServerFrameUtil.globalTrafficShapingHandler.releaseExternalResources();
    }

    /**
     * 线程池工厂
     */
    private  class PriorityThreadFactory implements ThreadFactory{
        private int _prio;
        private String _name;
        private AtomicInteger _threadNumber = new AtomicInteger(1);
        private ThreadGroup _group;
        /**
         *
         * @param name 线程池名
         * @param priority   线程池优先级
         */
        public PriorityThreadFactory(String name, int priority){
            _prio = priority;
            _name = name;
            _group = new ThreadGroup(_name);
        }
        @Override
        public Thread newThread(Runnable r){
            Thread t = new Thread(_group, r);
            t.setName(_name + "-"+"#-" + _threadNumber.getAndIncrement());
            t.setPriority(_prio);
            return t;
        }

        public ThreadGroup getGroup(){
            return _group;
        }
    }

}
